package com.reyco1.medusa.data
{
	import com.reyco1.medusa.events.DataEvent;
	import com.reyco1.medusa.abstract.SmartEventDispatcher;
	import com.reyco1.medusa.util.Delegate;
	
	import flash.events.Event;
	
	public class DataProvider extends SmartEventDispatcher
	{
		private var _data:Array = [];
		
		public function DataProvider(value:* = null)
		{
			super();	
			data = value == null ? [] : DataFormatter.formatData(value);
		}

		/* public */
		
		public function addItemAt(item:Object, index:uint):void 
		{
			checkIndex(index, data.length);
			data.splice(index, 0, item);
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
		}
		
		public function addItem(item:Object):void 
		{
			data.push(item);
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
		}
		
		public function addItemsAt(items:Object, index:uint):void 
		{
			checkIndex(index, data.length);
			var arr:Array = DataFormatter.formatData(items);
			data.splice.apply(data, [index ,0].concat(arr));
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
		}
		
		public function addItems(items:Object):void 
		{
			addItemsAt(items, data.length);
		}
		
		public function merge(newData:Object):void 
		{
			var arr:Array = DataFormatter.formatData(newData);
			var l:uint = arr.length;
			var startLength:uint = data.length;
			
			for (var i:uint=0; i<l; i++) 
			{
				var item:Object = arr[i];
				if (getItemIndex(item) == -1) 
				{
					data.push(item);
				}
			}
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
		}
		
		public function getItemAt(index:uint):Object 
		{
			checkIndex(index, data.length-1);
			return data[index];
		}
		
		public function getItemIndex(item:Object):int 
		{
			return data.indexOf(item);
		}
		
		public function removeItemAt(index:uint):Object 
		{
			checkIndex(index, data.length-1);
			var arr:Array = data.splice(index, 1);
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
			
			return arr[0];
		}
		
		public function removeItem(item:Object):Object 
		{
			var index:int = getItemIndex(item);
			if (index != -1) 
			{
				return removeItemAt(index);
			}
			return null;
		}
		
		public function removeAll():void 
		{
			data = [];
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
		}
		
		public function replaceItem(newItem:Object, oldItem:Object):Object 
		{
			var index:int = getItemIndex(oldItem);
			if (index != -1) 
			{
				return replaceItemAt(newItem,index);
			}
			return null;
		}
		
		public function replaceItemAt(newItem:Object,index:uint):Object 
		{
			checkIndex(index, data.length-1);
			
			var arr:Array = [data[index]];
			data[index] = newItem;
			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
			
			return arr[0];
		}
		
		public function sort(...sortArgs:Array):* 
		{
			var returnValue:Array = data.sort.apply(data, sortArgs);			
			dispatchEvent(new DataEvent(DataEvent.CHANGE));			
			return returnValue;
		}
		
		public function sortOn(fieldName:Object, options:Object = null):* 
		{
			var returnValue:Array = data.sortOn(fieldName, options);
			dispatchEvent(new DataEvent(DataEvent.CHANGE));
			return returnValue;
		}
		
		public function getItemsBy(key:String, value:*):Array
		{
			return data.filter(Delegate.create(filterItemByType, {key:key, value:value}));
		}	
		
		public function getItemBy(key:String, value:*):*
		{
			return data.filter(Delegate.create(filterItemByType, {key:key, value:value}))[0];
		}	
		
		private function filterItemByType(item:*, index:Number, array:Array, param:Object = null):Boolean
		{
			return item[param.key] == param.value;
		}
		
		public function contains(item:*):Boolean 
		{
			return (getItemIndex(item) > -1);
		}
		
		public function clone():DataProvider 
		{
			return new DataProvider(data);
		}
		
		public function toArray():Array 
		{
			return data.concat();
		}
		
		/* properties */
		
		[Bindable(event="dataChange")]
		public function get data():Array
		{
			return _data;
		}
		
		public function set data(value:Array):void
		{
			if( _data !== value)
			{
				_data = value;
				dispatchEvent(new DataEvent(DataEvent.CHANGE));
			}
		}
		
		public function get length():uint
		{
			return data.length;
		}
		
		/* private */
		
		protected function checkIndex(index:int, maximum:int):void 
		{
			if (index > maximum || index < 0) 
			{
				throw new Error("DataProvider index ("+index+") is not in acceptable range (0 - "+maximum+")");
			}
		}

	}
}